﻿using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;

namespace RM.Weighing
{
    /// <summary>
    /// LOG操作类
    /// </summary>
    public static class Log
    {
        #region Log辅助类

        private enum LogLevel
        {
            [DisplayText("ALL")]
            ALL = int.MinValue,
            [DisplayText("DEBUG")]
            DEBUG = 30000,
            [DisplayText("INFO")]
            INFO = 40000,
            [DisplayText("WARN")]
            WARN = 60000,
            [DisplayText("ERROR")]
            ERROR = 70000,
            [DisplayText("FATAL")]
            FATAL = 110000,
            [DisplayText("OFF")]
            OFF = int.MaxValue
        }

        private class LogInfo
        {
            public string Message { get; set; }
            public string DebugInfo { get; set; }
            public LogLevel Level { get; set; }
            public DateTime Time { get; set; }
        }

        private static readonly StringBuilder sb = new StringBuilder();

        private static string FilePath { get; set; }

        private static LogLevel Level { get; set; }

        private static string FileNamePrefix { get; set; }
        private static bool LogDebugInfo { get; set; }
        private static int MaxFileSize { get; set; }
        #endregion


        static Log()
        {
            Level = LogLevel.ALL;
            FileNamePrefix = "log";
            MaxFileSize = 1048576;
            FilePath = CurrentDir() + "Logs\\";

        }

        #region 外部访问

        /// <summary>
        /// 调试LOG
        /// </summary>
        /// <param name="message">消息</param>
        public static void Debug(string message)
        {
            AddLog(LogLevel.DEBUG, message);
        }

        /// <summary>
        /// 日常LOG
        /// </summary>
        /// <param name="message">消息</param>
        public static void Info(string message)
        {
            AddLog(LogLevel.INFO, message);
        }

        /// <summary>
        /// 警告LOG
        /// </summary>
        /// <param name="message">消息</param>
        public static void Warn(string message)
        {
            AddLog(LogLevel.WARN, message);
        }

        /// <summary>
        /// 错误LOG
        /// </summary>
        /// <param name="message">消息</param>
        public static void Error(string message)
        {
            AddLog(LogLevel.ERROR, message);
        }

        /// <summary>
        /// 崩溃LOG
        /// </summary>
        /// <param name="message">消息</param>
        public static void Fatal(string message)
        {
            AddLog(LogLevel.FATAL, message);
        }

        /// <summary>
        /// 警告LOG
        /// </summary>
        /// <param name="e">报错信息</param>
        public static void Warn(Exception e)
        {
            AddLog(LogLevel.WARN, e.Message.Trim(), e.StackTrace?.Trim());
        }

        /// <summary>
        /// 错误LOG
        /// </summary>
        /// <param name="e">报错信息</param>
        public static void Error(Exception e)
        {
            AddLog(LogLevel.ERROR, e.Message.Trim(), e.StackTrace?.Trim());
        }

        /// <summary>
        /// 崩溃LOG
        /// </summary>
        /// <param name="e">报错信息</param>
        public static void Fatal(Exception e)
        {
            AddLog(LogLevel.FATAL, e.Message.Trim(), e.StackTrace?.Trim());
        }
        #endregion


        #region 内部方法
        private static void AddLog(LogLevel level, params string[] message)
        {
            if (level >= Level)
            {
                LogInfo logInfo = new LogInfo();
                if (LogDebugInfo || level >= LogLevel.WARN)
                {
                    logInfo.DebugInfo = StackFrameLocationInfo();
                }

                logInfo.Level = level;
                logInfo.Time = DateTime.Now;
                logInfo.Message = string.Join(",", message);

                //if (UseThread)
                //{
                //    Queue.Enqueue(logInfo);
                //}
                //else
                //{
                //    WriteAtOnce(logInfo);
                //}

                WriteAtOnce(logInfo);
            }
        }

        private static void WriteAtOnce(LogInfo log)
        {

            sb.Clear();
            AddLog(log);
            bool flag = false;
            while (!flag)
            {
                try
                {
                    StreamWriter streamWriter = File.AppendText(GetLogFileName(log));
                    streamWriter.WriteLine(sb.ToString());
                    streamWriter.Flush();
                    streamWriter.Close();
                    streamWriter.Dispose();
                    flag = true;
                }
                catch (Exception)
                {
                }
            }
        }

        /// <summary>
        /// 获取自定义属性
        /// </summary>
        /// <typeparam name="T">类的type</typeparam>
        /// <param name="info"></param>
        /// <returns></returns>
        public static T GetCustomAttribute<T>(this FieldInfo info)
        {
            return (T)info.GetCustomAttributes(typeof(T), inherit: false).FirstOrDefault();
        }

        private static void AddLog(LogInfo log)
        {
            sb.Append(log.Time.ToString("G"));
            sb.Append("    ");
            sb.Append(DisplayText(log.Level));
            sb.AppendLine();
            sb.Append("   --->    ");
            if (log.DebugInfo != null)
            {
                sb.Append(log.DebugInfo);
                sb.Append("    ");
            }
            sb.Append(log.Message);
        }

        private static string DisplayText(LogLevel value)
        {
            DisplayTextAttribute customAttribute = GetCustomAttribute<DisplayTextAttribute>(value.GetType().GetField(value.ToString()));
            if (customAttribute == null)
            {
                return value.ToString();
            }

            return customAttribute.DisplayText;
        }

        private static string GetLogFileName(LogInfo log)
        {
            CreateDir(FilePath);
            int num = 0;
            string text2 = FilePath + log.Time.ToString("yyyyMMdd") + "." + FileNamePrefix;
            FileInfo fileInfo = new FileInfo(text2);
            while (fileInfo.Exists && fileInfo.Length >= MaxFileSize)
            {
                num++;
                text2 = FilePath + log.Time.ToString("yyyyMMdd") + "_" + num + "." + FileNamePrefix;
                fileInfo = new FileInfo(text2);
            }

            return text2;
        }

        private static string CurrentDir()
        {
            return DealPath(AppDomain.CurrentDomain.BaseDirectory);
        }
        private static void CreateDir(string directoryPath)
        {
            if (!Directory.Exists(directoryPath))
            {
                try
                {
                    Directory.CreateDirectory(directoryPath);
                }
                catch (Exception value)
                {
                    Console.WriteLine(value);
                }
            }
        }

        private static string DealPath(string path)
        {
            string a = "";
            if (!string.IsNullOrEmpty(path))
            {
                if (1 < path.Length)
                {
                    a = path.Substring(path.Length - 1, 1);
                }
            }
            if (!(a == "\\"))
            {
                return path + "\\";
            }

            return path;
        }
        private static string StackFrameLocationInfo()
        {
            StackFrame frame = new StackTrace(1, fNeedFileInfo: true).GetFrame(2);
            if (frame == null)
            {
                return "<UnknownClass>";
            }

            string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(frame.GetFileName());
            return $"<{fileNameWithoutExtension}.{frame.GetMethod()?.Name} -- L{frame.GetFileLineNumber()}>";
        }
        #endregion


        private class DisplayTextAttribute : Attribute
        {
            public DisplayTextAttribute(string text)
            {
                DisplayText = text;
            }

            public string DisplayText { get; }
        }
    }

}
